﻿using System;
using System.Collections.Generic;
using System.Configuration;
using System.IO;
using System.Reflection;
using System.Threading;
using System.Windows;
using System.Windows.Media;
using Elca.MvvmHelpers;
using Elca.MvvmHelpers.Services;

namespace ULSDeobfuscator {
    /// <summary>
    /// Interaction logic for App.xaml
    /// </summary>
    public partial class App {
        public const string Title = "ULS Deobfuscator";
        public const string FileChooser = "ChooseFile";
        public const string ComplexFilterManager = "ComplexFilters";
        public const string EditComplexFilter = "EditComplexFilter";
        public const string About = "About";
        public const string ColorRulesManager = "ComplexRules";
        public const string EditColorRule = "EditColorRule";

        public delegate void LogHandler(string msg, params object[] parameters);

        private static StreamWriter s_logStreamWriter;
        private static FileStream s_fileStream;

        public static LogHandler DebugLog { get; private set; }

        public App() {
            
            if (ConfigurationManager.AppSettings["EnableDebugLog"] == "yes") {

                string path = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "ULS Deobfuscator Debug Log - " + DateTime.Now.ToString("MM-dd-yyyy") + ".log");
                s_fileStream = new FileStream(path, FileMode.Append, FileAccess.Write, FileShare.ReadWrite);
                s_logStreamWriter = new StreamWriter(s_fileStream);

                DebugLog = RealLog;
                DebugLog("Application started");
            } else {
                DebugLog = StubLog;
            }

            AppDomain.CurrentDomain.UnhandledException += OnUnhandledException;
            AppDomain.CurrentDomain.AssemblyResolve += OnResolveAssembly;

            UIVisualizer viz = new UIVisualizer();

            ServiceRegistry.RegisterSingleton<IUIVisualizer>(viz);

            viz.Register(FileChooser, typeof(LogFileChooser));
            viz.Register(ComplexFilterManager, typeof(ComplexFiltersWindow));
            viz.Register(EditComplexFilter, typeof(EditComplexFilterWindow));
            viz.Register(About, typeof(AboutWindow));
            viz.Register(ColorRulesManager, typeof(ManageColorRulesWindow));
            viz.Register(EditColorRule, typeof(EditColorRuleWindow));

            ServiceRegistry.Register<IBrowseFileSystem>(typeof(BrowseFileSystem), ServiceLifetime.Multiple);
            ServiceRegistry.Register<IMessageToUser>(typeof(UserInteraction), ServiceLifetime.Singleton);

            DebugLog("Services were registered");

            GlobalSettings = SettingsSaver.LoadSettings();

            DebugLog("Settings were loaded");

            // Fix bug in versions prior to 1.8 where a rule to highlight errors in red was always added at each start-up

            int i = 0;
            bool defaultSeen = false;
            const string ruleDefaultName = "default rule for errors";
            while (i < GlobalSettings.ColorRules.Count) {

                ColorRule cr = GlobalSettings.ColorRules[i];
                // is rule i the one we added by default ?
                if ((String.IsNullOrEmpty(cr.Name) || cr.Name.Equals(ruleDefaultName)) && cr.ForegroundColor == Colors.Red &&
                    cr.Levels.Count == 1 && cr.Levels[0] == "High" && cr.IsPersistent &&
                    cr.Areas.Count == 0 && !cr.IsMultiLine && cr.BackgroundColor == null &&
                    cr.Categories.Count == 0 && String.IsNullOrEmpty(cr.Correlation) &&
                    cr.EventIDs.Count == 0 && String.IsNullOrEmpty(cr.Message) && cr.Processes.Count == 0) {

                    // It has our characteristics => let's assume it is

                    // if we've already changed one, delete the others
                    if (defaultSeen) {
                        GlobalSettings.ColorRules.RemoveAt(i);
                    } else {

                        cr.Name = ruleDefaultName;
                        // thanks to LMC for suggesting to highlight Monitorable instead of High
                        cr.Levels = new List<string> { "Monitorable" };
                        defaultSeen = true;
                    }
                } else {
                    // let's assume not
                    i++;
                }
            }

            // if no rule exists, add a default one
            if (GlobalSettings.ColorRules.Count == 0) {

                ColorRule defaultRule =
                        new ColorRule {
                            Levels = new List<string> {
                                "Monitorable"
                            },
                            ForegroundColor = Colors.Red,
                            IsPersistent = true,
                            Name = ruleDefaultName,
                        };
                GlobalSettings.ColorRules.Add(defaultRule);
            }

            // pre 1.12 => no rule about annotations
            if (GlobalSettings.Version == 1) {
                
                ColorRule annotationRule =
                        new ColorRule {
                            ForegroundColor = Colors.BurlyWood,
                            IsPersistent = true,
                            HasAnnotation = true,
                            Name = "Annotation",
                        };
                GlobalSettings.ColorRules.Add(annotationRule);

                GlobalSettings.Version = 2;
            }

            // adapt width and position to available width (on main screen)
            System.Drawing.Rectangle rect = System.Windows.Forms.Screen.GetWorkingArea(new System.Drawing.Point((int)GlobalSettings.MainWindowLeft, (int) GlobalSettings.MainWindowTop));
            int availableWidth = rect.Width;

            if (GlobalSettings.MainWindowLeft + GlobalSettings.MainWindowWidth > rect.Right) {
                // not enough room on screen

                if (availableWidth >= GlobalSettings.MainWindowWidth) {
                    // OK if we reduce the left position
                    GlobalSettings.MainWindowLeft = rect.Right - GlobalSettings.MainWindowWidth;
                } else {
                    
                    // even with a zero left coordinate, we are too big
                    GlobalSettings.MainWindowLeft = rect.Left;
                    GlobalSettings.MainWindowWidth = availableWidth;
                }
                
            }

            // don't start minimized
            if (GlobalSettings.MainWindowState == WindowState.Minimized) {
                GlobalSettings.MainWindowState = WindowState.Normal;
            }

        }

        private Assembly OnResolveAssembly(object sender, ResolveEventArgs args) {

            if (args.Name.StartsWith("ULSDeobfuscator.XmlSerializers")) {

                DebugLog("Application was requested the XML Serializer DLL");

                Assembly a = Assembly.GetExecutingAssembly();
                using (Stream s = a.GetManifestResourceStream("ULSDeobfuscator.Serializers.ULSDeobfuscator.XmlSerializers.dll")) {
                    
                    if (s != null) {
                        byte[] serializerBytes = new byte[s.Length];
                        s.Read(serializerBytes, 0, serializerBytes.Length);
                        Assembly serializer = Assembly.Load(serializerBytes);

                        DebugLog("Serializer DLL loaded");

                        return serializer;
                    }
                }
            }

            return null;
        }

        private void OnUnhandledException(object sender, UnhandledExceptionEventArgs unhandledExceptionEventArgs) {
            
            CrashLog("Unexpected error in ULS Deobfuscator: {0}", unhandledExceptionEventArgs.ExceptionObject);
        }

        private void CrashLog(string msg, params object[] parameters) {

            string path = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "ULS Deobfuscator crash.log");
            using (StreamWriter sw = new StreamWriter(path, true)) {

                sw.Write(DateTime.Now.ToString("MM-dd-yyyy HH-mm-ss "));
                sw.WriteLine(String.Format(msg, parameters));
            }
        }

        public static ProgramSettings GlobalSettings { get; set; }

        protected override void OnStartup(StartupEventArgs e) {

            DebugLog("In App.OnStartup");

            base.OnStartup(e);

            // if there are arguments, let the main window handle it
            string[] args = Environment.GetCommandLineArgs();

            if (args.Length == 1) {

                // no arg => request files from user

                IUIVisualizer viz = ServiceRegistry.Resolve<IUIVisualizer>();
                LogFileChooserViewModel lfvm = new LogFileChooserViewModel();
                lfvm.ChooseAdditionOrReplaceVisibility = Visibility.Collapsed;
                lfvm.IsWindowVisibleInTaskbar = true;

                if (viz.ShowDialog(App.FileChooser, lfvm) == false) {

                    Shutdown();
                    //MainWindow mw = new MainWindow();
                    //mw.ShowDialog();
                } else {
                    MainWindowViewModel.SetInitialFilesToRead(lfvm.SelectedLogFiles, lfvm.UserSelectedDate);
                }
            }

            DebugLog("Leaving App.OnStartup");
        }

        protected override void OnExit(ExitEventArgs e) {
            base.OnExit(e);

            // remove non-persistent color rules
            GlobalSettings.ColorRules.RemoveAll(cr => !cr.IsPersistent);

            SettingsSaver.SaveSettings(GlobalSettings);

            EndLog();
        }

        private static void StubLog(string msg, params object[] parameters) {
        }

        private static void RealLog(string msg, params object[] parameters) {
            
            if (s_logStreamWriter == null) {
                return;
            }

            s_logStreamWriter.Write(DateTime.Now.ToString("MM-dd-yyyy HH-mm-ss "));
#pragma warning disable 0618
            s_logStreamWriter.Write("({0}) ", AppDomain.GetCurrentThreadId());
#pragma warning restore 0618
            s_logStreamWriter.WriteLine(String.Format(msg, parameters));
            s_logStreamWriter.Flush();
        }

        private static void EndLog() {
            if (s_logStreamWriter != null) {
                s_logStreamWriter.Close();
            }
            if (s_fileStream != null) {
                s_fileStream.Close();
            }
        }

        public static string VersionNumber {
            get {
                Assembly a = Assembly.GetExecutingAssembly();
                Version v = a.GetName().Version;

                return $"{v.Major}.{v.Minor}";

            }
        }
    }
}
